#!/bin/python

#
#Xithyl, Test Driven Development for C
#Copyright (C) 2012  Stepan Henek
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import ctypes

from . import lib

def memchr(src, c, n):
    """
    Searches for the first occurrence of the character c (an unsigned char) in the first n bytes of the string pointed to by the argument str.

    Returns a pointer pointing to the first matching character, or null if no match was found.
    """

    # Specify parameters
    lib.memchr.argtypes = [ ctypes.c_void_p, ctypes.c_int, ctypes.c_size_t ]
    lib.memchr.restype = ctypes.c_void_p

    # Call the function
    return lib.memchr(src, c, n)

def memcmp(str1, str2, n):
    """
    Compares the first n bytes of str1 and str2. Does not stop comparing even after the null character (it always checks n characters).

    Returns zero if the first n bytes of str1 and str2 are equal. Returns less than zero or greater than zero if str1 is less than or greater than str2 respectively.
    """

    # Specify parameters
    lib.memcmp.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ]
    lib.memcmp.restype = ctypes.c_int

    # Call the function
    return lib.memcmp(str1, str2, n)

def memcpy(str1, str2, n):
    """
    Copies n characters from str2 to str1. If str1 and str2 overlap the behavior is undefined.

    Returns the argument str1.
    """

    # Specify parameters
    lib.memcpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ]
    lib.memcpy.restype = ctypes.c_void_p

    # Call the function
    return lib.memcpy(str1, str2, n)

def memmove(str1, str2, n):
    """
    Copies n characters from str2 to str1. If str1 and str2 overlap the information is first completely read from str2 and then written to str1 so that the characters are copied correctly.

    Returns the argument str1.
    """

    # Specify parameters
    lib.memmove.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t ]
    lib.memmove.restype = ctypes.c_void_p

    # Call the function
    return lib.memmove(str1, str2, n)

def memset(Str, c, n):
    """
    Copies the character c (an unsigned char) to the first n characters of the string pointed to by the argument str.

    The argument str is returned.
    """

    # Specify parameters
    lib.memset.argtypes = [ ctypes.c_void_p, ctypes.c_int, ctypes.c_size_t ]
    lib.memset.restype = ctypes.c_void_p

    # Call the function
    return lib.memset(Str, c, n)

def strcat(str1, str2):
    """
    Appends the string pointed to by str2 to the end of the string pointed to by str1. The terminating null character of str1 is overwritten. Copying stops once the terminating null character of str2 is copied. If overlapping occurs, the result is undefined.

    The argument str1 is returned.
    """

    # Specify parameters
    lib.strcat.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcat.restype = ctypes.c_char_p

    # Call the function
    return lib.strcat(str1, str2)

def strncat(str1, str2, n):
    """
    Appends the string pointed to by str2 to the end of the string pointed to by str1 up to n characters long. The terminating null character of str1 is overwritten. Copying stops once n characters are copied or the terminating null character of str2 is copied. A terminating null character is always appended to str1. If overlapping occurs, the result is undefined.

    The argument str1 is returned.
    """

    # Specify parameters
    lib.strncat.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t ]
    lib.strncat.restype = ctypes.c_char_p

    # Call the function
    return lib.strncat(str1, str2, n)


def strchr(Str, c):
    """
    Searches for the first occurrence of the character c (an unsigned char) in the string pointed to by the argument str. The terminating null character is considered to be part of the string.

    Returns a pointer pointing to the first matching character, or null if no match was found.
    """

    # Specify parameters
    lib.strchr.argtypes = [ ctypes.c_char_p, ctypes.c_int ]
    lib.strchr.restype = ctypes.c_char_p

    # Call the function
    return lib.strchr(Str, c)

def strcmp(str1, str2):
    """
    Compares the string pointed to by str1 to the string pointed to by str2.

    Returns zero if str1 and str2 are equal. Returns less than zero or greater than zero if str1 is less than or greater than str2 respectively.
    """

    # Specify parameters
    lib.strcmp.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcmp.restype = ctypes.c_int

    # Call the function
    return lib.strcmp(str1, str2)

def strncmp(str1, str2, n):
    """
    Compares at most the first n bytes of str1 and str2. Stops comparing after the null character.

    Returns zero if the first n bytes (or null terminated length) of str1 and str2 are equal. Returns less than zero or greater than zero if str1 is less than or greater than str2 respectively.
    """

    # Specify parameters
    lib.strncmp.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, c_char_p.c_size_t ]
    lib.strncmp.restype = ctypes.c_int

    # Call the function
    return lib.strncmp(str1, str2, n)

def strcoll(str1, str2):
    """
    Compares string str1 to str2. The result is dependent on the LC_COLLATE setting of the location.

    Returns zero if str1 and str2 are equal. Returns less than zero or greater than zero if str1 is less than or greater than str2 respectively.
    """
    # Specify parameters
    lib.strcoll.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcoll.restype = ctypes.c_int

    # Call the function
    return lib.strcoll(str1, str2)

def strcpy(str1, str2):
    """
    Copies the string pointed to by str2 to str1. Copies up to and including the null character of str2. If str1 and str2 overlap the behavior is undefined.

    Returns the argument str1.
    """

    # Specify parameters
    lib.strcpy.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcpy.restype = ctypes.c_char_p

    # Call the function
    return lib.strcpy(str1, str2)

def strncpy(str1, str2, n):
    """
    Copies up to n characters from the string pointed to by str2 to str1. Copying stops when n characters are copied or the terminating null character in str2 is reached. If the null character is reached, the null characters are continually copied to str1 until n characters have been copied.

    Returns the argument str1.
    """

    # Specify parameters
    lib.strncpy.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, c_char_p.c_size_t ]
    lib.strncpy.restype = ctypes.c_char_p

    # Call the function
    return lib.strncpy(str1, str2, n)


def strcspn(str1, str2):
    """
    Finds the first sequence of characters in the string str1 that does not contain any character specified in str2.

    Returns the length of this first sequence of characters found that do not match with str2.
    """

    # Specify parameters
    lib.strcspn.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcspn.restype = ctypes.c_size_t

    # Call the function
    return lib.strcspn(str1, str2)

def strerror(errnum):
    """
    Searches an internal array for the error number errnum and returns a pointer to an error message string.

    Returns a pointer to an error message string.
    """

    # Specify parameters
    lib.strcspn.argtypes = [ ctypes.c_int ]
    lib.strcspn.restype = ctypes.c_char_p

    # Call the function
    return lib.strerror(errnum)

def strlen(Str):
    """
    Computes the length of the string str up to but not including the terminating null character.

    Returns the number of characters in the string.
    """

    # Specify parameters
    lib.strlen.argtypes = [ ctypes.c_char_p ]
    lib.strlen.restype = ctypes.c_size_t

    # Call the function
    return lib.strlen(Str)

def strpbrk(str1, str2):
    """
    Finds the first character in the string str1 that matches any character specified in str2.

    A pointer to the location of this character is returned. A null pointer is returned if no character in str2 exists in str1.
    """

    # Specify parameters
    lib.strpbrk.argtypes = [ ctypes.c_char_p, c_char_p.c_char_p ]
    lib.strpbrk.restype = ctypes.c_char_p

    # Call the function
    return lib.strpbrk(str1, str2)

def strrchr(str1, c):
    """
    Searches for the last occurrence of the character c (an unsigned char) in the string pointed to by the argument str. The terminating null character is considered to be part of the string.

    Returns a pointer pointing to the last matching character, or null if no match was found.
    """

    # Specify parameters
    lib.strrchr.argtypes = [ ctypes.c_char_p, c_char_p.c_int ]
    lib.strrchr.restype = ctypes.c_char_p

    # Call the function
    return lib.strrchr(str1, c)

def strspn(str1, str2):
    """
    Finds the first sequence of characters in the string str1 that contains any character specified in str2.

    Returns the length of this first sequence of characters found that match with str2.
    """

    # Specify parameters
    lib.strspn.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strspn.restype = ctypes.c_size_t

    # Call the function
    return lib.strspn(str1, str2)

def strstr(str1, str2):
    """
    Finds the first occurrence of the entire string str2 (not including the terminating null character) which appears in the string str1.

    Returns a pointer to the first occurrence of str2 in str1. If no match was found, then a null pointer is returned. If str2 points to a string of zero length, then the argument str1 is returned.
    """

    # Specify parameters
    lib.strstr.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strstr.restype = ctypes.c_char_p

    # Call the function
    return lib.strstr(str1, str2)

def strtok(str1, str2):
    """
    Breaks string str1 into a series of tokens. If str1 and str2 are not null, then the following search sequence begins. The first character in str1 that does not occur in str2 is found. If str1 consists entirely of characters specified in str2, then no tokens exist and a null pointer is returned. If this character is found, then this marks the beginning of the first token. It then begins searching for the next character after that which is contained in str2. If this character is not found, then the current token extends to the end of str1. If the character is found, then it is overwritten by a null character, which terminates the current token. The function then saves the following position internally and returns.

    Subsequent calls with a null pointer for str1 will cause the previous position saved to be restored and begins searching from that point. Subsequent calls may use a different value for str2 each time.

    Returns a pointer to the first token in str1. If no token is found then a null pointer is returned.
    """

    # Specify parameters
    lib.strtok.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strtok.restype = ctypes.c_char_p

    # Call the function
    return lib.strtok(str1, str2)

def strxfrm(str1, str2, n):
    """
    Transforms the string str2 and places the result into str1. It copies at most n characters into str1 including the null terminating character. The transformation occurs such that strcmp applied to two separate converted strings returns the same value as strcoll applied to the same two strings. If overlapping occurs, the result is undefined.

    Returns the length of the transformed string (not including the null character).
    """

    # Specify parameters
    lib.strxfrm.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t ]
    lib.strxfrm.restype = ctypes.c_size_t

    # Call the function
    return lib.strxfrm(str1, str2)

################################################################# Extra

def ffs(val):
    """
    This function finds the first (least significant) bit set in the input value.
    
    Returns:
        The ffs() function returns the position of the first (least significant) 
        bit set in the word val, or 0 if no bits are set. The least significant bit is position 1
    """

    # Specify parameters
    lib.ffs.argtypes = [ ctypes.c_int ]
    lib.ffs.restype = ctypes.c_int

    # Call the function
    return lib.ffs(val)

def ffsl(val):
    """
    Same as ffs(), for an argument of type long.
    """

    # Specify parameters
    lib.ffsl.argtypes = [ ctypes.c_long ]
    lib.ffsl.restype = ctypes.c_int

    # Call the function
    return lib.ffsl(val)

def ffsll(val):
    """
    Same as ffs(), for an argument of type long long.
    """

    # Specify parameters
    lib.ffsll.argtypes = [ ctypes.c_longlong ]
    lib.ffsll.restype = ctypes.c_int

    # Call the function
    return lib.ffsll(val)

#################################################################

def memccpy(dest, src, val, Len):
    """
    Copy memory area.

    The memccpy() function copies no more than len bytes from memory area src to memory area dest, stopping when the character val is found.

    Returns:
    The memccpy() function returns a pointer to the next character in dest after val, or NULL if val was not found in the first len characters of src.
    """

    # Specify parameters
    lib.memccpy.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_size_t ]
    lib.memccpy.restype = ctypes.c_void_p

    # Call the function
    return lib.memccpy(dest, src, val, Len)

def memmem(s1, len1, s2, len2):
    """
    The memmem() function finds the start of the first occurrence of the substring s2 of length len2 in the memory area s1 of length len1.

    Returns:
    The memmem() function returns a pointer to the beginning of the substring, or NULL if the substring is not found. If len2 is zero, the function returns s1.
    """

    # Specify parameters
    lib.memmem.argtypes = [ ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_size_t ]
    lib.memmem.restype = ctypes.c_void_p

    # Call the function
    return lib.memmem(s1, len1, s2, len2)

def memrchr(src, val, Len):
    """
    The memrchr() function is like the memchr() function, except that it searches backwards from the end of the len bytes pointed to by src instead of forwards from the front. (Glibc, GNU extension.)

    Returns:
    The memrchr() function returns a pointer to the matching byte or NULL if the character does not occur in the given memory area.
    """

    # Specify parameters
    lib.memrchr.argtypes = [ ctypes.c_void_p, ctypes.c_int, ctypes.c_size_t ]
    lib.memrchr.restype = ctypes.c_void_p

    # Call the function
    return lib.memrchr(src, val, Len)

def strcasecmp(s1, s2):
    """
    Compare two strings ignoring case.

    The strcasecmp() function compares the two strings s1 and s2, ignoring the case of the characters.

    Returns:
    The strcasecmp() function returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2. A consequence of the ordering used by strcasecmp() is that if s1 is an initial substring of s2, then s1 is considered to be "less than" s2.
    """

    # Specify parameters
    lib.strcasecmp.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcasecmp.restype = ctypes.c_int

    # Call the function
    return lib.strcasecmp(s1, s2)

def strcasestr(s1, s2):
    """
    The strcasestr() function finds the first occurrence of the substring s2 in the string s1. This is like strstr(), except that it ignores case of alphabetic symbols in searching for the substring. (Glibc, GNU extension.)

    Returns:
    The strcasestr() function returns a pointer to the beginning of the substring, or NULL if the substring is not found. If s2 points to a string of zero length, the function returns s1.
    """

    # Specify parameters
    lib.strcasestr.argtypes = [ ctypes.c_char_p, ctypes.c_char_p ]
    lib.strcasestr.restype = ctypes.c_char_p

    # Call the function
    return lib.strcasestr(s1, s2)

def strchrnul(s, c):
    """
    The strchrnul() function is like strchr() except that if c is not found in s, then it returns a pointer to the null byte at the end of s, rather than NULL. (Glibc, GNU extension.)

    Returns:
    The strchrnul() function returns a pointer to the matched character, or a pointer to the null byte at the end of s (i.e., s+strlen(s)) if the character is not found.
    """

    # Specify parameters
    lib.strchrnul.argtypes = [ ctypes.c_char_p, ctypes.c_int ]
    lib.strchrnul.restype = ctypes.c_char_p

    # Call the function
    return lib.strchrnul(s1, s2)

def strdup(s1):
    """
    Duplicate a string.

    The strdup() function allocates memory and copies into it the string addressed by s1, including the terminating null character.

    Warning:
    The strdup() function calls malloc() to allocate the memory for the duplicated string! The user is responsible for freeing the memory by calling free().
    Returns:
    The strdup() function returns a pointer to the resulting string dest. If malloc() cannot allocate enough storage for the string, strdup() will return NULL.
    Warning:
    Be sure to check the return value of the strdup() function to make sure that the function has succeeded in allocating the memory!
    """

    # Specify parameters
    lib.strdup.argtypes = [ ctypes.c_char_p ]
    lib.strdup.restype = ctypes.c_char_p

    # Call the function
    return lib.strdup(s1)

def strlcat(dst, src, siz):
    """
    Concatenate two strings.

    Appends src to string dst of size siz (unlike strncat(), siz is the full size of dst, not space left). At most siz-1 characters will be copied. Always NULL terminates (unless siz <= strlen(dst)).

    Returns:
    The strlcat() function returns strlen(src) + MIN(siz, strlen(initial dst)). If retval >= siz, truncation occurred.

    Appends src to string dst of size siz (unlike strncat(), siz is the full size of dst, not space left). At most siz-1 characters will be copied. Always NULL terminates (unless siz <= strlen(dst)).
    """

    # Specify parameters
    lib.strlcat.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t ]
    lib.strlcat.restype = ctypes.c_size_t

    # Call the function
    return lib.strlcat(dst, src, siz)

def strlcpy(dst, src, siz):
    """
    Copy a string.

    Copy src to string dst of size siz. At most siz-1 characters will be copied. Always NULL terminates (unless siz == 0).

    Returns:
    The strlcpy() function returns strlen(src). If retval >= siz, truncation occurred.

    Copy src to string dst of size siz. At most siz-1 characters will be copied. Always NULL terminates (unless siz == 0).
    """

    # Specify parameters
    lib.strlcpy.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t ]
    lib.strlcpy.restype = ctypes.c_size_t

    # Call the function
    return lib.strlcpy(dst, src, siz)

def strlwr(src):
    """
    Convert a string to lower case.

    The strlwr() function will convert a string to lower case. Only the upper case alphabetic characters [A .. Z] are converted. Non-alphabetic characters will not be changed.

    Returns:
    The strlwr() function returns a pointer to the converted string.
    """

    # Specify parameters
    lib.strlwr.argtypes = [ ctypes.c_char_p ]
    lib.strlwr.restype = ctypes.c_char_p

    # Call the function
    return lib.strlwr(src)

def strncasecmp(s1, s2, Len):
    """
    Compare two strings ignoring case.

    The strncasecmp() function is similar to strcasecmp(), except it only compares the first len characters of s1.

    Returns:
    The strncasecmp() function returns an integer less than, equal to, or greater than zero if s1 (or the first len bytes thereof) is found, respectively, to be less than, to match, or be greater than s2. A consequence of the ordering used by strncasecmp() is that if s1 is an initial substring of s2, then s1 is considered to be "less than" s2.
    """

    # Specify parameters
    lib.strncasecmp.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, c_char_p.c_size_t ]
    lib.strncasecmp.restype = ctypes.c_int

    # Call the function
    return lib.strncasecmp(s1, s2, Len)

def strnlen(src, Len):
    """
    Determine the length of a fixed-size string.

    The strnlen function returns the number of characters in the string pointed to by src, not including the terminating '\0' character, but at most len. In doing this, strnlen looks only at the first len characters at src and never beyond src+len.

    Returns:
    The strnlen function returns strlen(src), if that is less than len, or len if there is no '\0' character among the first len characters pointed to by src.
    """

    # Specify parameters
    lib.strnlen.argtypes = [ ctypes.c_char_p, c_char_p.c_size_t ]
    lib.strnlen.restype = ctypes.c_size_t

    # Call the function
    return lib.strnlen(src, Len)

def strrev(s):
    """
    Reverse a string.

    The strrev() function reverses the order of the string.

    Returns:
    The strrev() function returns a pointer to the beginning of the reversed string.
    """

    # Specify parameters
    lib.strrev.argtypes = [ ctypes.c_char_p, c_char_p.c_int ]
    lib.strrev.restype = ctypes.c_char_p

    # Call the function
    return lib.strrev(s)

def strsep(sp, delim):
    """
    Parse a string into tokens.

    The strsep() function locates, in the string referenced by *sp, the first occurrence of any character in the string delim (or the terminating '\0' character) and replaces it with a '\0'. The location of the next character after the delimiter character (or NULL, if the end of the string was reached) is stored in *sp. An ``empty'' field, i.e. one caused by two adjacent delimiter characters, can be detected by comparing the location referenced by the pointer returned in *sp to '\0'.

    Returns:
    The strsep() function returns a pointer to the original value of *sp. If *sp is initially NULL, strsep() returns NULL.
    """

    # Specify parameters
    lib.strsep.argtypes = [ ctypes.POINTER(ctypes.c_char_p), ctypes.c_char_p ]
    lib.strsep.restype = ctypes.c_char_p

    # Call the function
    return lib.strsep(sp, delim)

def strtok_r(string, delim, last):
    """
    Parses string into tokens.

    strtok_r parses string into tokens. The first call to strtok_r should have string as its first argument. Subsequent calls should have the first argument set to NULL. If a token ends with a delimiter, this delimiting character is overwritten with a '\0' and a pointer to the next character is saved for the next call to strtok_r. The delimiter string delim may be different for each call. last is a user allocated char* pointer. It must be the same while parsing the same string. strtok_r is a reentrant version of strtok().

    Returns:
    The strtok_r() function returns a pointer to the next token or NULL when no more tokens are found.
    """

    # Specify parameters
    lib.strtok_r.argtypes = [ ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p) ]
    lib.strtok_r.restype = ctypes.c_char_p

    # Call the function
    return lib.strtok_r(string, delim, last)

def strupr(s):
    """
    Convert a string to upper case.

    The strupr() function will convert a string to upper case. Only the lower case alphabetic characters [a .. z] are converted. Non-alphabetic characters will not be changed.

    Returns:
    The strupr() function returns a pointer to the converted string. The pointer is the same as that passed in since the operation is perform in place.
    """

    # Specify parameters
    lib.strupr.argtypes = [ ctypes.c_char_p ]
    lib.strupr.restype = ctypes.c_char_p

    # Call the function
    return lib.strupr(s)
